/*
 * Copyright (c) 2008-2021 浩瀚深度 All Rights Reserved.
 *
 * <p>FileName: DataSourceServiceImpl.java</p>
 *
 * @author zz
 * @date 2024/2/4
 * @version 1.0
 * History:
 * v1.0.0 2024/2/4 Create
 */
package com.sqlweb.sqlweb.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.sqlweb.sqlweb.base.BusinessException;
import com.sqlweb.sqlweb.base.Result;
import com.sqlweb.sqlweb.base.ResultCode;
import com.sqlweb.sqlweb.config.DatabaseConfig;
import com.sqlweb.sqlweb.config.DynamicDataSourceConfig;
import com.sqlweb.sqlweb.entity.DataSourceConfig;
import com.sqlweb.sqlweb.mapper.DataSourceConfigMapper;
import com.sqlweb.sqlweb.service.DataSourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>Title: DataSourceServiceImpl</p>
 * <p>Description: </p>
 *
 * @author zz
 */
@Slf4j
@Service
public class DataSourceServiceImpl implements DataSourceService {
    @Autowired
    private DataSourceConfigMapper dataSourceConfigMapper;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private DynamicDataSourceConfig dynamicDataSourceConfig;
    @Autowired
    private DatabaseConfig databaseConfig;

    @Override
    public Integer addDataSourceConfig(DataSourceConfig dataSourceConfig) {
        if (null == dataSourceConfig) {
            throw new BusinessException("入参不能为空");
        }
        List<DatabaseConfig.DbType> dbTypes = databaseConfig.getDbType();
        if (CollectionUtil.isNotEmpty(dbTypes)) {
            Map<Integer, String> dbTypeMap = dbTypes.stream()
                    .collect(Collectors.toMap(
                            DatabaseConfig.DbType::getType,
                            DatabaseConfig.DbType::getDriverClass,
                            // 在键冲突时选择后者
                            (existing, replacement) -> replacement
                    ));
            if (dbTypeMap.containsKey(dataSourceConfig.getDbType())) {
                dataSourceConfig.setDriverClassName(dbTypeMap.get(dataSourceConfig.getDbType()));
                //拼接jdbcUrl 示例:jdbc:postgresql://10.10.4.178:5432/olp_dnv
                String jdbcUrl;
                switch (dataSourceConfig.getDbType()) {
                    case 1:
                    case 3:
                        // GreenPlum
                        // PostgreSQL
                        jdbcUrl = String.format("jdbc:postgresql://%s:%d/%s", dataSourceConfig.getHost(), dataSourceConfig.getPort(), dataSourceConfig.getDbName());
                        break;
                    case 2:
                        // MySQL
                        jdbcUrl = String.format("jdbc:mysql://%s:%d/%s", dataSourceConfig.getHost(), dataSourceConfig.getPort(), dataSourceConfig.getDbName());
                        break;
                    default:
                        //return new Result(ResultCode.ERROR, "不支持的数据库类型");
                        throw new BusinessException("不支持的数据库类型");
                }
                dataSourceConfig.setJdbcUrl(jdbcUrl);
            }
        }
        DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.url(dataSourceConfig.getJdbcUrl()).username(dataSourceConfig.getUsername())
                .password(dataSourceConfig.getPassword()).driverClassName(dataSourceConfig.getDriverClassName());
        DataSource dataSource = dataSourceBuilder.build();
        //入库
        dataSourceConfigMapper.insert(dataSourceConfig);
        //内存
        dynamicDataSourceConfig.addDataSource(dataSourceConfig.getId(), dataSource);
        return dataSourceConfig.getId();
    }

    @Override
    public void removeDataSourceConfig(DataSourceConfig dataSourceConfig) {
        if (null == dataSourceConfig) {
            throw new BusinessException("入参不能为空");
        }
        if (null == dataSourceConfig.getId()) {
            throw new BusinessException("数据源ID不能为空");
        }
        if (null == dataSourceConfig.getName()) {
            throw new BusinessException("数据源名称不能为空");
        }
        dynamicDataSourceConfig.removeDataSource(dataSourceConfig.getId());
        dataSourceConfigMapper.deleteById(dataSourceConfig.getId());
    }

    @Override
    public DataSourceConfig getDataSourceConfig(String name) {
        return dataSourceConfigMapper.selectById(name);
    }

    @Override
    public List<DataSourceConfig> selectAll() {
        List<DataSourceConfig> configs = dataSourceConfigMapper.selectAll();
        if (CollectionUtil.isNotEmpty(configs)) {
            configs.forEach(config -> {
                List<String> tables = findTablesByDataSource(config);
                config.setTables(tables);
            });
        }
        return configs;
    }

    public List<String> findTablesByDataSource(DataSourceConfig dataSourceConfig) {
        try {
            switch (dataSourceConfig.getDbType()) {
                case 1:
                    return findTablesForPostgreSQL(dataSourceConfig);
                case 2:
                    return findTablesForMySQL(dataSourceConfig);
                case 3:
                    return findTablesForGreenplum(dataSourceConfig);
                default:
                    throw new IllegalArgumentException("未知的数据库类型");
            }
        } catch (Exception e) {
            log.error("数据表信息获取失败:" + e);
        }
        return null;
    }

    private List<String> findTablesForPostgreSQL(DataSourceConfig dataSourceConfig) throws SQLException {
        List<String> tables = new ArrayList<>();
        // 构造PostgreSQL的JDBC连接字符串
        String url = String.format("jdbc:postgresql://%s:%d/%s",
                dataSourceConfig.getHost(),
                dataSourceConfig.getPort(),
                dataSourceConfig.getDbName());

        // 使用try-with-resources来自动关闭资源
        try (
                Connection conn = DriverManager.getConnection(url, dataSourceConfig.getUsername(), dataSourceConfig.getPassword());
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE';")
        ) {
            while (rs.next()) {
                tables.add(rs.getString("table_name"));
            }
        }
        return tables;
    }


    private List<String> findTablesForMySQL(DataSourceConfig dataSourceConfig) throws SQLException {
        List<String> tables = new ArrayList<>();
        // 构造MySQL的JDBC连接字符串
        String url = String.format("jdbc:mysql://%s:%d/%s",
                dataSourceConfig.getHost(),
                dataSourceConfig.getPort(),
                dataSourceConfig.getDbName());

        // 使用try-with-resources来自动关闭资源
        try (
                Connection conn = DriverManager.getConnection(url, dataSourceConfig.getUsername(), dataSourceConfig.getPassword());
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = '" + dataSourceConfig.getDbName() + "';")
        ) {
            while (rs.next()) {
                tables.add(rs.getString("table_name"));
            }
        }
        return tables;
    }


    private List<String> findTablesForGreenplum(DataSourceConfig dataSourceConfig) throws SQLException {
        List<String> tables = new ArrayList<>();
        // 构造Greenplum的JDBC连接字符串
        String url = String.format("jdbc:pivotal:greenplum://%s:%d;DatabaseName=%s",
                dataSourceConfig.getHost(),
                dataSourceConfig.getPort(),
                dataSourceConfig.getDbName());

        // 使用try-with-resources来自动关闭资源
        try (
                Connection conn = DriverManager.getConnection(url, dataSourceConfig.getUsername(), dataSourceConfig.getPassword());
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';")
        ) {
            while (rs.next()) {
                tables.add(rs.getString("table_name"));
            }
        }
        return tables;
    }

}
